#!/usr/bin/env python3
# -*- coding: utf-8 -*-
print("\n\n#========================内置基本类型=========")

print('\n\n#==============常量和变量==============')
#PI看着像常量，但python没有机制来保证PI不会被改变，只是一种约定，大写就是常量
PI = 3.14
pi = 3.14

print('\n\n#==============整数==============')
#python对整数大小没限制
i = 1
i = 100
i = -8080
i = 0xff00   # 16进制
i = 0xa5b4c3d2
print(type(i))

print('\n\n#==============浮点数==============')
#python对浮点数大小也没限制，但超出一定范围就直接表示为inf（无限大）
f = 1.23
f = -9.01
f = 1.23e8  #1.23 x 10的8次方
f = 1.2e-5  #0.000012
print(type(f))

#整数和浮点数的算数运算
x = 10
x = x + 2  #12
x = 10 -2  #8
x = 10*2  #20
x = 10/3  #3.3333  注意这个和Java的区别
x = 10//3 #3   这个是地板除
x = 10%3  #1


print('\n\n#==============布尔值==============')
#定义
b = True
print(type(b))
print(b)
b = False
print(b)

#其他类型到布尔的转换：
x = 0
if x:  #只要x是非零数值、非空字符串、非空list等，就判断为True，否则为False
    print(x)

#比较运算符，和布尔运算and or not
if 3 > 2 and 3 < 4:
    print('3大于2 and 3小于4')

if 3 > 2 or 3 < 4:
    print('3大于2 or 3小于4')

if not 3 < 2:
    print('3不小于2')

age = 18
if age > 18:
    print('>18')
elif age > 10:
    print('>10')
else:
    print('fuck')





print('\n\n#==============空值None，即java里的null==============')
s = None
print(type(s))
print(s)



print('\n\n#==============字符串==============')
s = 'abc'
print(type(s))
s = "abc"
s = "a'b'c"
s = 'a"b"c'
print(s)
s = 'I\'m \"ok\"'
print(s)
s = '===\n===\t===\\==='
print(s)
print("\n\n#============r''表示内部字符串不转义==========")
s = r'\n\t\/hahhaha'
print(s)

print("#===多行字符串===")
s = '''第一行
... 第二行
... 第三行
...第四行
'''
print(s)

print("\n\n#============字符串编码和bytes类型（b开头）==========")
#建议参考http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431664106267f12e9bef7ee14cf6a8776a479bdec9b9000
print(ord('a')) #97---ord方法只能获取单个字符的整数表示
print(chr(20013)) #中---chr将整数转换为一个字符
print('\u4e2d\u6587') # 输出中文俩字，字符串常量中直接写unicode或者字符串，是等价的

s = 'ABC'.encode('ascii')  #把字符串转换为bytes类型
print(s)  #b'ABC'
s = b'ABC'.decode('ascii')  #把bytes类型转换为字符串
print(s)

s = '短短的'.encode('utf-8')  #把字符串转换为bytes类型
print(s)  #b'\xe7\x9f\xad\xe7\x9f\xad\xe7\x9a\x84'
s = b'\xe7\x9f\xad\xe7\x9f\xad\xe7\x9a\x84'.decode('utf-8')  #把bytes类型转换为字符串
print(s)
#注意，b''中的内容只能是ascii字符，不可能出现 b'中文' 的形式

print('ABC 的长度是%d' %(len('ABC')))  #3
print('哈哈哈哈 的长度是%d' %(len('哈哈哈哈'))) #3
print('中文俩字转成bytes的长度是%d' %(len('中文'.encode('utf-8')))) #6
#所以一个中文对应的utf-8编码是3个字节

print("\n\n#============字符串格式化==========")
s = 'Hi %s, you score is %.2f, you are %d years old, fuck you!%%' % ('tom', 43.5, 32)
print(s)
#如果不知道类型，都可以用s%，这个是万能的，其他类型会被转换为字符串
s = 'Hi %s, you score is %s, you are %s years old, fuck you!' % ('tom', 43.5, 32)
print(s)

'''
%s 字符串占位符
%d 整型
%x 16位整型
%f 浮点型
%.2f 小数点保留两位
%% 代表%，要输出%号时，需要这么写

'''

print("\n\n#============字符串的操作==========")

#replace(old, new)
print('-----------文本替换')
s = "AAAAhahahahah"
s = s.replace('ha', "哈")
print(s)

print('------------全小写')
s = s.lower()
print(s)

print('-------------去前后空格')
s = "   hahha\ndddd\n";
print(s.strip()) # 把末尾的'\n'删掉
print('-------------')

s.startswith('aa')

print('\n\n#==============类型转换==============')

print('#===字符串转为数值===')
s = int('123')  #123
s = float('12.34')

print('#===数值转为字符串===')
s = str(123)
s = str(123.34)

print('#===各种类型转为bool===')
s = bool(1) #True
s = bool('') #False


print("\n\n#==================列表：list===========================")
'''
访问快：list
插入和删除慢：使用deque（双向链表）
'''
#可变列表
#注意，不要用list作为变量名，因为有个list函数
data = [] #空列表，len=0
data = ['me', 'you', 'him']
data = list()  #空列表，len=0
data = list((13,4,5,3)) #tuple转换为list
data = list(range(5));  #0,1,2,3,4
print(data)
print(len(data))

#访问下标
print(data[0])
data[0] = 'who'
print(data[0])

#取最后一个：注意，加负号就是倒数的意思，倒数第一个，就是-1
#所以数组的有效下标是： [-len, len-1]
print(data[-1])
print(data[-3])

#添加和删除元素
data.append('jack')
print(len(data))  #4

lastElement = data.pop()  #  返回并删除最后一个元素
print(lastElement)
print(len(data)) #3

#注意插入和替换的区别，替换是直接用[下标]来设置
#如果插入的位置>=len，则插入最后一个
#如果插入的位置<=(-len)，则插入第一个
data.insert(1, 'linken') #指定下标和要插入的值，下标的意思是，在下标为几的元素之前插入
print(data)

data.insert(-100, 'linken') #在最后一个元素之前插入，即作为倒数第二个
print(data)

#排序
print('#\n=====list的排序：还必须是orderale的，字符串是不可排序的！！！？？')
data = [1,3,4,3,5,56,4,3,5,2]
data.sort()
print(data)

print("\n\n#==================链表：deque===========================")
'''
访问快：list
插入和删除慢：使用deque（双向链表），此类由collections模块提供，用于链表，队列，堆栈
'''
from collections import deque
q = deque(['a', 'b', 'c'])
q.append('x')
q.appendleft('y')
q.insert(1, 'dd')  #在指定下标的前面插入
q.insert(-2, 'eee')
print(q)  #deque(['y', 'dd', 'a', 'b', 'eee', 'c', 'x'])
#pop(), appendleft()和popleft()

print("\n\n#==================元组：tuple===========================")
#不可变列表：只能访问，不能增删改
data = ()  #空元组
data = ('me', 'you', 'him')
print(data[2])

#只有一个元素的元组，是个问题，因为(1)，是运算表达式里的小括号1，所以得加个逗号
data = (1,)
print(len(data))  #1


print("\n\n#==================循环===========================")

print('===循环1')
data = ('me', 'you', 'him')
for name in data:
    print(name)

print('===循环2')
sum = 0
for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
    sum = sum + x
print(sum)

print('===循环3')
print(type(range(3)))  #<class 'range'>

print('===循环4')
sum = 0
n = 99
while n > 0:
    sum = sum + n
    n = n - 2
print(sum)  #2500

print("\n\n#==================字典：dict===========================")
data = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print(data['Michael'])  #下标方式访问，如果key不存在，则报错
print(data.get('Michael')) #get方式访问，如果key不存在，则返回None

if 'tom' in data:
    print(data['tom'])
else:
    data['tom'] = 54
    print('添加了一个key==>tom，值是%s' %(data['tom']))


print("\n\n#==================字典：defaultdict--带默认值===========================")
'''
如果key不存在，会抛出异常，通常这是我们不希望的，所以collections模块提供了
一个defaultdict，当访问的key不存在，返回一个指定的默认值
'''
from collections import defaultdict
dd = defaultdict(lambda: 'N/A')  #不存在的key，返回默认值N/A
dd['key1'] = 'abc'
print(dd['key1']) # key1存在
print(dd['key2']) # key2不存在，返回默认值

print("\n\n#==================字典：OrderedDict--按key排序的===========================")
'''
使用dict时，Key是无序的。在对dict做迭代时，我们无法确定Key的顺序。
如果要保持Key的顺序，可以用OrderedDict
===可以用这个来实现一个FIFO（先进先出）的dict，容量超限时，删除最早进来的
===注意，是按照插入的顺序排序，而不是按照key排序
'''
from collections import OrderedDict
d = dict([('a', 1), ('c', 2), ('b', 3)])
print(d)  #不是按插入顺序排序的
od = OrderedDict([('a', 1), ('c', 2), ('b', 3)])
print(od) #按acb排序

print("\n\n#==================dict的子类：Counter（计数器）===========================")
from collections import Counter
c = Counter()
for ch in 'programming':
    c[ch] = c[ch] + 1
print(c)



print("\n\n#==================集合：set，就是没有value的dict===========================")
#不可放入可变对象，因为可变对象是unhashable的
s = set([1, 2, 3])
print(s)

s.add(4)
print(s)

s.remove(4)
print(s)

print("\n\n#============= 切片slice：适用于list，tuple，str ===================")
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']

print('#======截取子串')
''' 切片形式1 ： 截取子串
L[start: end]   返回的还是个list
start：---从哪儿开始取，省略则表示从开头算
end：---取到此下标的前一个，省略则表示len
'''
print(L[0:1])  #Michael
print(L[:2])  #'Michael', 'Sarah'
print(L[:])   #整个列表的复制
print(L[-2:])  #'Bob', 'Jack'
print(L[-2:5])  #'Bob', 'Jack'
print(L[-2:-1]) #Bob'

print('#======间隔取')
''' 切片形式2 ： 间隔取
L[:total:step]   返回的还是个list
total---在前几个中取，省略则表示在所有元素中取
step---下标每次加几，省略则表示1, 也就是挨个取，否则就是下标每次加个step
'''
L = list(range(100))
print(L[:10:2]) # 0，2，4，6，8
print(L[:10:]) #等价于L[:10:1]
#print(L[::])  #在所有元素中，下标每次加1


print("\n\n#============= 遍历 ===================")
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']

print('\n#===1')
for key in L:
    print(key)

print('\n#===2')
for ch in 'ABC':
    print(ch)

print('\n#===3')
dataMap = {'aa':1, 'vv':'dd'}
for value in dataMap.values():
    print(value)

print('\n#===4')
dataMap = {'aa':1, 'vv':'dd'}
for key in dataMap.keys():
    print('%s==>%s' % (key, dataMap[key]))


print('\n#===5 取出下标')
for i,ch in enumerate('ABC'):  #enumerate把可迭代对象变成了索引-元素对
    print(i, ch)

print('\n#===6')
for x, y in [(1, 1), (2, 4), (3, 9)]:
    print(x, y)

print('\n#===6 遍历dict的key和value')
d = {'x': 'A', 'y': 'B', 'z': 'C' }
for k, v in d.items():  #items()可以同时迭代key和value
    print(k, '=', v)


print("\n\n#============= 列表生成式 ===================")
L = list(range(1, 11))

'''
相当于：简单形式
L = []
for x in range(1, 11):
    L.append(x * x)
'''
L = [x*x for x in range(1, 11)]
print(L)  #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

'''
相当于：加判断
L = []
for x in range(1, 11):
    if x%2 == 0 :
        L.append(x * x)
'''
L = [x*x for x in range(1, 11) if x%2 == 0]
print(L) #[4, 16, 36, 64, 100]

''' 双层循环
相当于：
L = []
for n in 'XYZ':
    for m in 'ABC'
        L.append(m+n)
'''
L = [m + n for m in 'ABC' for n in 'XYZ']
print(L) #[4, 16, 36, 64, 100] #['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

'''
列出当前目录下的所有文件和目录名，可以通过一行代码实现
'''
import os # 导入os模块，模块的概念后面讲到
L = [d for d in os.listdir('.')]
print(L)


print("\n\n#============= 列表生成器 ===================")

''' 定义方式1：通过列表生成式
把一个列表生成式的[]改成()，就是生成器generator
===优点是，如果列表太大，内存放不下，可以动态生成
===调用： next(g) ，每次调用，内部游标都加1，就能返回遍历的下一个值
===遍历：基本上使用方式就是遍历，一般不用next
'''
g = (x*x for x in range(1, 11))
print(type(g))  #<class 'generator'>
print(next(g))
print(next(g))
print(next(g))
print("\n#用for来遍历")
for n in g:
    print(n)

''' 定义方式2：通过函数的语法（用yield代替return）
这种方式也可以通过next调用或者for来遍历，一般是遍历
'''
#定义个generator，返回1,3,5
def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)
print("\n#用for来遍历")
for n in odd():
    print(n)

#输出杨辉三角
# print('\n#========#输出杨辉三角')
# #辅助函数，一共输出total个数，在下标为index处，输出value， 其他都是0
# def output(total, ids, value):
#     for n : range(0, total):
#         if(n == index):
#             print(value)
#         else:
#             print(0)
#
# def yanghui_triangles(level):
#     #注意，有level层，每层需要输出2×level-1个数，
#     for n in range(1, level):
#         if n % 2 != 0:
#             print()



#yanghui_triangles(6)


print("\n\n#============= 自定义类List的鸭子类型 ===================")
'''
实现__iter__和__next__的类就是可迭代的，Iterable，
会导致：
from collections import Iterable
ininstance(obj, Iterable)为true

注意：
===list、dict、str虽然是Iterable，却不是Iterator，可以for
===生成器都是Iterator对象，可以调用next()函数
===iter()方法可以将Iterable变成Iterator
===Iterator是惰性序列，列表的全部元素是每次迭代到了再计算，而不是提前存在内存里
===可以通过list(iterator)把一个Iterator转换为list

讲解：
Python的Iterator对象表示的是一个数据流，Iterator对象可以被next()函数调用并不
断返回下一个数据，直到没有数据时抛出StopIteration错误。可以把这个数据流看做是
一个有序序列，但我们却不能提前知道序列的长度，只能不断通过next()函数实现按需
计算下一个数据，所以Iterator的计算是惰性的，只有在需要返回下一个数据时它才会
计算。
'''
class Book(object):
    def __init__(self, name):
        self.name = name

class Books(object):

    def __init__(self):
        self.books = [Book('AA'), Book('BB'), Book('CC')]
        self.currentIndex = 0

    def __iter__(self):
        return self;   #返回一个可迭代的对象，即实现了__next__的对象

    def __next__(self):
        if(self.currentIndex == len(self.books)):
            raise StopIteration()  # 停止迭代：for循环总得有个结束啊
        oldIdx = self.currentIndex;
        self.currentIndex += 1;
        return self.books[oldIdx]


    #此方法可以传入的参数n，可能是下标（整数），也可能是切片（slice类型）
    def __getitem__(self, n):
        if isinstance(n, int):
            return self.books[n]
        if isinstance(n, slice):
            return '不支持切片访问'

books = Books();
for book in books:
    print(book.name)
print('可下标访问的对象：%s' %(books[1].name))

print("\n\n#============= 自定义类dict的鸭子类型 ===================")

'''
想让你自定义的类型可以像dict那样d['key']来访问，需要实现：
__setitem__()和__delitem__()
'''


print("\n\n#============= 枚举 ===================")
'''
value属性则是自动赋给成员的int常量，默认从1开始计数
'''
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
print(Month.Jan)
print('\n#===遍历枚举类型')
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)


#更精确的控制value
from enum import Enum, unique

@unique  #@unique装饰器可以帮助我们检查保证没有重复值
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

print('\n#===继承自Enum，并使用装饰器@unique')
day1 = Weekday.Mon
print(day1)
print(Weekday['Tue'])
print(Weekday.Tue.value)
print(Weekday(1))
print(day1 == Weekday(1))


print("\n\n#============= 元类：metaclass ===================")
'''
参考：http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319106919344c4ef8b1e04c48778bb45796e0335839000
'''
#
